﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Text.RegularExpressions;

namespace Assembler
{
    public partial class formPrincipal : Form
    {
        Stream streamAssembly;
        StreamReader lector;
        StreamWriter escritor;

        
        String[] instrucciones = new String[50];    //lista de instrucciones en excel
        String[] rom;

        List<String[]> codigo_assembly = new List<String[]>();

        List<String> codigo_binario = new List<string>();

        List<String> Data = new List<String>();

        Dictionary<String,int> variables = new Dictionary<String,int>();   //diccionario que guarda todos los labels junto con su posicion de memoria en la ram
        Dictionary<String, int> labels = new Dictionary<String, int>();
        Dictionary<String, String> variablesValores = new Dictionary<String, String>(); //guarda los labels con sus valores.

        public formPrincipal()
        {
            InitializeComponent();
        }

        private void formPrincipal_Load(object sender, EventArgs e)
        {
            //instrucciones = File.ReadAllLines("comandos.txt");
            instrucciones = Properties.Resources.comandos.Split('\n');
            rom = Properties.Resources.ROM.Split('\n');

            for (int i = 0; i < instrucciones.Length; i++)
            {
                if(instrucciones[i].IndexOf('\r') >= 0)
                    instrucciones[i] = instrucciones[i].Remove(instrucciones[i].IndexOf('\r'));
            }
        }

        private void botonAssembly_Click(object sender, EventArgs e)
        {
            /*
            Mostrar el cuadro de dialogo, abrir el archivo y despues empezar a leerlo:
            */
            if (openAssemblyDialog.ShowDialog() == DialogResult.OK) {

                streamAssembly = openAssemblyDialog.OpenFile();
                lector = new StreamReader(streamAssembly);                
                Crear_DataCode();
                Crear_BinaryCode();
                streamAssembly.Close();
            }
        }

        private void Crear_DataCode()   //Crea una lista con todas las lineas de codigo assembly, separadas en arreglos de tamaño 3
        {
            //escritor = new StreamWriter(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)+"/resultado.txt", false);
            escritor = new StreamWriter("output.txt", false);
            int puntero = 0;    //valor de memoria en la ram en el cual se van a guardar los valores en Data:

            String aux;
            bool data = true;
            int direccion_label = 2;

            aux = lector.ReadLine();
            codigo_binario.Add("0000100000000000000000000"); //instruccion para restarle uno al Stack Pointer y parta apuntando a lo mas abajo de la RAM.
            while (data && (lector.Peek() > -1))        //Aqui asignamos las variables de Data:
            {
                aux = QuitarComentario(aux);
                if (!aux.Contains("DATA"))
                {
                    Data.Add(aux);
                    if (aux != "")
                    {
                        aux = Regex.Replace(aux, @"\s+", " ");

                        String[] espacio = aux.Split(new char[] { ' ' });

                        if (espacio.Length == 1)        //se carga el valor en algún registro y se guarda ese valor de registro en la ram
                        {
                            codigo_binario.Add(AgregarLit(instrucciones[2], espacio[0]));    //MOV A,Lit...Lit=espacio[0]
                            direccion_label++;
                            codigo_binario.Add(AgregarLit(instrucciones[6], puntero.ToString()));
                            direccion_label++;
                        }

                        else if (espacio.Length == 2)
                        {
                            variables.Add(espacio[0], puntero);    //se guarda el nombre de la variable junto con el puntero correspondiente en el dictionary (labels)
                            variablesValores.Add(espacio[0], espacio[1]);   //se guarda el nombre de la variable junto con su valor

                            codigo_binario.Add(AgregarLit(instrucciones[2], espacio[1]));   //MOV A,Lit...Lit=espacio[1]
                            direccion_label++;
                            codigo_binario.Add(AgregarLit(instrucciones[6], puntero.ToString()));   //MOV (Dir),A...donde Dir = puntero
                            direccion_label++;
                        }
                        puntero++;
                    }
                    
                }
                aux = lector.ReadLine();
                if (aux.Contains("CODE:"))
                    data = false;
            }

            
                while (lector.Peek() > -1)      //Aqui separamos lo que viene despues de CODE:
                {

                    aux = lector.ReadLine();
                    aux = QuitarComentario(aux);

                    String[] coma = new string[2];
                    String[] instruccion = new string[3];

                    if (aux != "")
                    {
                        aux = aux.Trim();
                        if (aux.Contains(':'))
                        {
                            instruccion[0] = aux;
                            instruccion[1] = "";
                            instruccion[2] = "";
                            aux = aux.Trim(':');
                            labels.Add(aux, direccion_label);
                        }

                        else
                        {
                            String Instruccion;
                            String Parametros;
                            if (aux.Contains("CALL") || aux.Contains("PUSH"))
                            {
                                Instruccion = aux.Substring(0, 4);
                                Parametros = aux.Substring(4, aux.Length - 4);
                            }
                            else
                            {
                                Instruccion = aux.Substring(0, 3);
                                Instruccion = Instruccion.Trim();
                                Parametros = aux.Substring(3, (aux.Length - 3));
                            }
                            Parametros = Parametros.Replace(" ", String.Empty);
                            coma = Parametros.Split(new Char[] { ',' });

                            if (coma.Length == 1)
                            {
                                instruccion[0] = Instruccion;
                                instruccion[1] = coma[0];
                                instruccion[2] = "";
                            }
                            else
                            {
                                instruccion[0] = Instruccion;
                                if (coma[0] != null)
                                    instruccion[1] = coma[0];
                                if (coma[1] != null)
                                    instruccion[2] = coma[1];
                            }

                            if (instruccion[0] == "CALL")
                                direccion_label += 4;
                            else if (instruccion[0] == "RET" || instruccion[0] == "POP")
                                direccion_label += 2;
                            else
                                direccion_label++;
                        }

                        codigo_assembly.Add(instruccion);
                    }
                }
            
            lector.Close();
        } 
 
       /* private String CodeToBinary(String[] s)// identifica los diferentes casos (mov, add, etc) para llamar a los metodos respectivos que los transforman en binario
        {
            String unocero = "";

            if (s[0] == "MOV")
                unocero = Mov(s[1], s[2]);
            else if (s[0] == "ADD")
                unocero = Add(s[1], s[2]);
            else if (s[0] == "SUB")
                unocero = Sub(s[1], s[2]);
            else if (s[0] == "INC")
                unocero = Inc(s[1]);
            else if (s[0] == "DEC")
                unocero = Dec(s[1]);
            else if (s[0] == "JMP")
                unocero = Jmp(s[1]);
            else if (s[0] == "JEQ")
                unocero = Jeq(s[1]);
            else if (s[0] == "JNE")
                unocero = Jne(s[1]);
            else if (s[0] == "CMP")
                unocero = Cmp(s[1],s[2]);
            else if (s[0] == "JGE")
                unocero = Jge(s[1]);
            else if (s[0] == "JLT")
                unocero = Jlt(s[1]);
            else if (s[0] == "PUSH")
                unocero = Push(s[1]);
            else if (s[0] == "POP")
            {
                unocero = Pop(s[1], 1);
                unocero = Pop(s[1], 2);
            }
            else if (s[0] == "IN")
                unocero = In(s[1]);
            else if (s[0] == "RET")
            {
                unocero = Ret(1);
                unocero = Ret(2);
            }
            else if (s[0] == "CALL")
            {
                unocero = Call(s[1], 1);
                unocero = Call(s[1], 2);
                unocero = Call(s[1], 3);
                unocero = Call(s[1], 4);
                unocero = Call(s[1], 5);
            }
            return unocero;
        } */

        private void Crear_BinaryCode()// crea la lista de codigo ya en binario... podemos usar este mismo para escribir el txt de la rom
        {
            
            foreach (String[] s in codigo_assembly)
            {
                string unocero="";

                if (s[0] == "MOV")
                {
                    unocero = Mov(s[1], s[2]);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "ADD")
                {
                    unocero = Add(s[1], s[2]);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "SUB")
                {
                    unocero = Sub(s[1], s[2]);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "INC")
                {
                    unocero = Inc(s[1]);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "DEC")
                {
                    unocero = Dec(s[1]);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "JMP")
                {
                    unocero = Jmp(s[1]);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "JEQ")
                {
                    unocero = Jeq(s[1]);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "JNE")
                {
                    unocero = Jne(s[1]);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "CMP")
                {
                    unocero = Cmp(s[1], s[2]);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "JGE")
                {   unocero = Jge(s[1]);
                    if (unocero != "") 
                {
                    //escritor.WriteLine(unocero);
                    codigo_binario.Add(unocero);
                }}
                else if (s[0] == "JLT")
                {   unocero = Jlt(s[1]);
                    if (unocero != "") 
                {
                    //escritor.WriteLine(unocero);
                    codigo_binario.Add(unocero);
                }}
                else if (s[0] == "PUSH")
                {
                    unocero = Push(s[1]);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "POP")
                {
                    unocero = Pop(s[1], 1);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                    unocero = Pop(s[1], 2);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "IN")
                {
                    unocero = In(s[1]);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "RET")
                {
                    unocero = Ret(1);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                    unocero = Ret(2);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                }
                else if (s[0] == "CALL")
                {
                    unocero = Call(s[1], 1);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                    unocero = Call(s[1], 2);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                    unocero = Call(s[1], 3);
                    if (unocero != "")
                    {
                        //escritor.WriteLine(unocero);
                        codigo_binario.Add(unocero);
                    }
                    unocero = Call(s[1], 4);
                    if (unocero != "")
                    {
                        codigo_binario.Add(unocero);
                    }
                }    
            }
           // escritor.Close();
            reemplazarYEscribir();
           // this.Close();
        }
 
        private void reemplazarYEscribir(){ // reemplaza en el arreglo de la ROM las instrucciones y escribe el resultado.
            for (int i = 0; i < codigo_binario.Count; i++ )
            {
                rom[i + 14] = rom[i + 14].Replace("0000000000000000000000000",codigo_binario[i]);
            }

            for (int i = 0; i < rom.Length; i++ )
            {
                escritor.WriteLine(rom[i]);
            }

            escritor.Close();
            MessageBox.Show("Archivo escrito correctamente.");
            this.Close();
        }

        #region Metodos Instrucciones
        //Metodos que transforman de instruccion a binario
        // no estan puestas las palabras (1 y 0), solo esta separado en los casos posibles
        private String Mov(String par1, String par2)
        {
            String bin = "algo";
            if (par1 == "A")
            {
                if (par2 == "B")
                { bin = AgregarCeros(instrucciones[0]); }   // MOV A,B
                else if (par2.Contains("("))
                {
                    if (par2=="(B)")
                    { bin = AgregarCeros(instrucciones[8]); }     // MOV A,(B)
                    else
                    { bin = AgregarLit(instrucciones[4],par2); }     // MOV A,(Dir)
                }
                else
                { bin = AgregarLit(instrucciones[2], par2) ; }   //MOV A,Lit

            }
            else if (par1 == "B")
            {
                if (par2 == "A")
                { bin = AgregarCeros(instrucciones[1]); }   // mov B,A
                else if (par2.Contains("("))
                {
                    if (par2 == "(B)")
                    { bin = AgregarCeros(instrucciones[9]); } // MOV B,(B)
                    else
                    { bin = AgregarLit(instrucciones[5], par2); } // MOV B,(Dir)
                }
                else
                { bin = AgregarLit(instrucciones[3], par2); }   // MOV B,Lit
            }
            else // cuando parte con ()
            {
                if (par2 == "A" && par1 == "(B)")
                { 
                    bin = AgregarCeros(instrucciones[10]); // MOV (B),A
                }
                else if (par2 == "A")       
                {
                    bin = AgregarLit(instrucciones[6], par1);   // MOV (Dir),A
                } 
                else { bin = AgregarLit(instrucciones[7], par1); } // MOV (Dir),B
            }

            return bin;
        }

        private String Add(String par1, String par2)
        {
            String bin = "";

            if (par1 == "A")
            {
                if (par2 == "B")
                { bin = AgregarCeros(instrucciones[11]); } // ADD A,B
                else if(par2.Contains("("))
                {
                    if (par2 == "(B)")
                    bin = AgregarCeros(instrucciones[15]); // ADD A,(B)
                    else
                    bin = AgregarLit(instrucciones[14], par2); } // ADD A,(Dir)
                else
                { bin = AgregarLit(instrucciones[13],par2); } // ADD A,Lit
            }
            else if (par1 == "B")
            {
                bin = AgregarCeros(instrucciones[12]);     // ADD B,A
            }
            else
            { bin = AgregarLit(instrucciones[16], par1); }  //ADD (Dir)


            return bin;
        }

        private String Sub(String par1, String par2)
        {
            String bin = "";

            if (par1 == "A")
            {
                if (par2 == "B")
                { bin = AgregarCeros(instrucciones[17]); }    //SUB A,B

                else if (par2.Contains("("))
                {
                    if (par2 == "(B)")
                        bin = AgregarCeros(instrucciones[21]);    //SUB A,(B)
                    else
                        bin = AgregarLit(instrucciones[20], par2);    //SUB A,(Dir)
                } 
                else
                { bin = AgregarLit(instrucciones[19],par2); } // SUB A,Lit
            }
            else if (par1 == "B")
            {
                bin = AgregarCeros(instrucciones[18]);     //SUB B,A
            }
            else 
            { bin = AgregarLit(instrucciones[22], par1); }    //SUB (Dir)


            return bin;
        }

        private String Inc(String par1)
        {
            String bin = "";

            if (par1 == "A")
             bin = AgregarLit(instrucciones[23], "1");    //INC A

            else
             bin = AgregarCeros(instrucciones[24]);    //INC B
            return bin;
        }

        private String Dec(String par1)
        {
            String bin = "";

            if (par1 == "A")
                bin = AgregarLit(instrucciones[25], "1");    //DEC A

            else
                bin = AgregarCeros(instrucciones[26]);    //DEC B
            return bin;
        }

        private String Jmp(String label)
        {
            String bin = AgregarLit(instrucciones[28],labels[label].ToString()); //JMP
            return bin;
        }

        private String Jeq(String label)
        {
            String bin = AgregarLit(instrucciones[27], labels[label].ToString()); // JEQ
            return bin;
        }

        private String Jne(String label)
        {
            String bin = AgregarLit(instrucciones[29], labels[label].ToString()); ; //JNE
            return bin;
        }

        private String Jge(String label)
        {
            String bin = AgregarLit(instrucciones[32], labels[label].ToString()); ; //JGE
            return bin;
        }

        private String Jlt(String label)
        {
            String bin = AgregarLit(instrucciones[33], labels[label].ToString()); ; //JLT
            return bin;
        }

        private String Cmp(String par1, String par2)
        {
            String bin;
            if (par2 == "B")
            { bin = AgregarCeros(instrucciones[30]); }    //CMP A,B
            else
            { bin = AgregarLit(instrucciones[31], par2); } // CMP A,Lit
            return bin;
        }

        private String Push(String par1)
        {
            String bin;
            if (par1 == "A")
                bin = AgregarCeros(instrucciones[34]);      //PUSH A

            else
                bin = AgregarCeros(instrucciones[35]);      //PUSH B
            return bin;
        }

        private String Pop(String par1, int n)
        {

            String bin;
            if (par1 == "A")
            {
                if (n == 1)
                    bin = AgregarCeros(instrucciones[36]);      //POP A
                else
                    bin = AgregarCeros(instrucciones[37]);
            }
            else
            {
                if (n == 1)
                    bin = AgregarCeros(instrucciones[38]);      //POP B
                else
                    bin = AgregarCeros(instrucciones[39]);
            }
            return bin;
        }

        private String In(String par1)
        {
            String bin;
            if (par1 == "A")
                bin = AgregarCeros(instrucciones[40]);      //IN A

            else
                bin = AgregarCeros(instrucciones[41]);      //IN B
            return bin;
        }

        private String Ret(int n)
        {
            String bin;
            if (n == 1)
                bin = AgregarCeros(instrucciones[42]);      //RET
            else
                bin = AgregarCeros(instrucciones[43]);
            return bin;
        }

        private String Call(String Dir, int i)
        {
            if (i == 1)
                return AgregarCeros(instrucciones[44]);     //  Mem[SP]  =  PC
            else if (i == 2)
                return AgregarCeros(instrucciones[45]);     //  A = Mem[SP]
            else if (i == 3)
                return AgregarLit(instrucciones[46],"4");   //  Mem[SP]  =  A+4, SP--
            else
                return AgregarLit(instrucciones[47], labels[Dir].ToString());   //  PC = Dir
        }

        #endregion

        private String QuitarComentario(String linea)  // elimina cualquier posible comentario de la linea recibida
        {
            if (linea.Contains(" //"))
            {
                linea = linea.Remove(linea.IndexOf(" //"));

            }
            else if (linea.Contains("//"))
            {
                linea = linea.Remove(linea.IndexOf("//"));
            }
            return linea;
        }

        private String AgregarLit(String instruccion, String literal)   //agrega la direccion o el literal correspondiente a la instruccion recibida...retorna la palabra completa de 25 bits
        {
            literal = EnBinario(literal);
            instruccion = instruccion + literal;
            return instruccion;
        }

        private String AgregarCeros(String instruccion) //agrega ceros a la instruccion para completar los 25 bits
        {
            instruccion = instruccion + "00000000";
            return instruccion;
        }

        private bool EsLabel(String comparado)  //Verifica que el string recibido este dentro de nuestra coleccion de labels
        {
            return variables.ContainsKey(comparado);
        }

        private String EnBinario(string numero) //toma un numero en base 2, 10 o 16 y lo devuelve en un binario de 8 bits
        {
            String binario = "";
            numero = numero.Trim();

            if (numero.Contains("("))
                numero = numero.Substring(1, numero.Length - 2);

            if (variables.ContainsKey(numero))
                numero = variables[numero].ToString();

            int ultimo = numero.Length - 1;

            if (numero[ultimo] == 'b')          //binario
            {
                binario = numero.Remove(ultimo);
            }

            else if (numero[ultimo] == 'h')     //hexadecimal
            {
                numero = numero.Remove(ultimo);
                int aux = Convert.ToInt32(numero, 16);
                binario = Convert.ToString(aux, 2);
            }

            else if (numero[ultimo] == 'd')     //decimal
            {
                numero = numero.Remove(ultimo);
                int aux = Convert.ToInt32(numero, 10);
                binario = Convert.ToString(aux, 2);
            }

            else                                //asumimos que es decimal
            {
                int aux = Convert.ToInt32(numero, 10);
                binario = Convert.ToString(aux, 2);
            }



            int ceros = 8 - binario.Length;     //Cuento los 0 que faltan para completar los 8 de nuestros literales y direcciones

            while (ceros > 0)
            {
                binario = "0" + binario;
                ceros--;
            }

            return binario;
        }
        
    }
}
